import copy

# 安全性算法
def bank_safe(Pn): # 目前的可用资源、所有进程需要的资源、所有进程已拥有的资源、进程数量
    global Available, Need, Allocation
    Work = copy.deepcopy(Available) # 尝试分配的临时情况
    Finish = [False]*Pn # 记录所有可能完成的进程
    # 可能完成的任务个数
    safeNum = 0

    for count in range(Pn):
        for i in range(Pn): # 一个一个进程判断
            isSafe = True  # 记录当前进程是否可以加入安全序列
            if not Finish[i]: # 目前看还不能完成，检测当前work状态下能否完成
                for ch in Work.keys(): # work的key是资源名，和need的value对应
                    if Need[i][ch] > Work[ch]: # 比较Need[i]（当前检查的进程需要的该资源数量）与Work的各元素值
                        isSafe = False # 如果资源不够，这进程就不安全了
                        break

                if isSafe:
                    safeNum+=1  # 加入安全序列（这里可以改成记进程分配顺序）
                    Finish[i] = True  # 该进程可能完成
                    # 该进程完成之后把资源释放，下次循环就可用看到有了这些资源之后别的进程行不行了
                    for ch2 in Work.keys():
                        Work[ch2] = Work[ch2] + Allocation[i][ch2]
    # 判断安全序列里面的元素个数，如果等于进程数Pn 说明所有进程都安全
    if safeNum==Pn:
        print('安全状态，允许分配')
        return True
    else:
        print('不安全状态，不允许分配')
        return False


# 模拟进程请求资源
def bank_request(processId,requestDict): # 进程序号，request资源字典
    global Available, Need, Allocation

    for ch in requestDict.keys():
        if requestDict[ch] > Need[processId][ch]:
            print("请求资源量与记录的进程最大所需资源冲突")
            return False

    for ch2 in requestDict.keys():
        if requestDict[ch2] > Available[ch2]:
            print("可用资源不足")
            return False

    # 分配资源
    oldAvailable=copy.deepcopy(Available)
    for ch3 in requestDict.keys():
        Available[ch3] = Available[ch3] - requestDict[ch3]

    allo = Allocation[processId]  # allo记录目前给该进程分配的资源
    oldAllo=copy.deepcopy(allo)
    for ch4 in allo.keys():
        allo[ch4] = allo[ch4] + requestDict[ch4]

    need = Need[processId]  # need记录目前该进程还需要的资源
    oldneed=copy.deepcopy(need)
    for ch5 in need.keys():
        need[ch5] = need[ch5] - requestDict[ch5]

    # 进行系统执行安全性检查,如果（分配资源后）为安全状态，分配成功，否则还原到分配前的状态
    if bank_safe(Pn):
        print("请求完成时分配情况Allocate:", Allocation)
        return True
    else:
        Available=oldAvailable
        Allocation[processId]=oldAllo
        Need[processId]=oldneed
        return False

'''
def bank_need(Allocation,Max):
    Need = []
    cn = 0
    for allo in Allocation:
        tmp = copy.deepcopy(allo)  # 深拷贝一个同规格字典用于存放need
        for ch in allo.keys():
            tmp[ch] = Max[cn][ch] - allo[ch]
        Need.append(tmp)
        cn += 1
    return Need

def bank_Available(Available,Allocation):
    for allo in Allocation:
        for ch in allo.keys():
            Available[ch] = Available[ch] - allo[ch]
'''
def getAllocation(Need):
    Allocation=copy.deepcopy(Need)
    for allo in Allocation:
        for k in allo.keys():
            allo[k]=0
    return Allocation

if __name__ == '__main__':
    global Available, Need, Allocation
    # 不用输入的测试代码
    # Pn = 5
    # Available = {'A':17, 'B':5, 'C':20}
    # Max = [{'A':5, 'B':5, 'C':9},
    #               {'A':5, 'B':3, 'C':6},
    #               {'A':4, 'B':0, 'C':11},
    #               {'A':4, 'B':2, 'C':5},
    #              {'A':4, 'B':2, 'C':4}]
    # Allocation = [{'A':2, 'B':1, 'C':2},
    #                     {'A':4, 'B':0, 'C':2},
    #                    {'A':4, 'B':0, 'C':5},
    #                    {'A':2, 'B':0, 'C':4},
    #                    {'A':3, 'B':1, 'C':4}]
    Pn = int(input("请输入要模拟的进程数量n:"))
    Available = eval(input("请输入系统初始资源信息，输入格式为dict,如'{'A':10, 'B':10, 'C':10}' :"))
    # 每个进程的最大需求（如果目前占用的是0，这个就是need）
    print("---------即将开始输入Max的信息---------' ")
    Max = []
    for i in range(Pn):
        print("请输入第",i,"个进程对每种资源的最大需求，输入格式为dict,如'{'A':1, 'B':2, 'C':3}' ")
        max = eval(input(" :"))
        Max.append(max)
    # 每个进程目前占用的
    '''
    print("---------即将开始输入Allocation的信息---------' ")
    Allocation = []
    for i in range(Pn):
        print("请输入第",i,"个进程对每种资源的已占用的情况，输入格式为dict,如'{'A':1, 'B':2, 'C':3}' ")
        # fix:
        allo = eval(input(" :"))
        Allocation.append(allo)
    '''
    Need = Max
    Allocation = getAllocation(Need)
    # Need = bank_need(Allocation, Max) # 由需要的和已经有的计算还剩的（need）
    # bank_Available(Available,Allocation) # 由总量和需要的计算还剩没用的（新Available）

    while True:
        print("---------即将开始模拟进程请求资源---------")
        processId = int(input("请输入要模拟的进程序号n:"))
        requestDict = eval(input("请输入请求资源信息，输入格式如'{'A':10, 'B':10, 'C':10}' :"))
        bank_request(processId, requestDict)  # 模拟请求资源